package studia.figlewicz.math.library;

import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

public class ZarzadcaProjekcji {
	
	
	private MagazynMonteCarlo magazynWynikow;
	public void createMagazynWynikow(int size) {
		magazynWynikow = new MagazynMonteCarlo(size);
	}
	public MagazynMonteCarlo getMagazynWynikow() {
		return magazynWynikow;
	}
	
	private GeneratorLiczbLosowych generator;
	public GeneratorLiczbLosowych getGeneratorLiczbLosowych() {
		return generator;
	}
	public void createGeneratorLiczbLosowych() {
		this.generator = new GeneratorLiczbLosowych();
	}
	
	private Set<ProjekcjaModelu> projekcje = new LinkedHashSet<ProjekcjaModelu>();
	public Set<ProjekcjaModelu> getProjekcje() {
		return projekcje;
	}
	public void setProjekcje(Set<ProjekcjaModelu> projekcje) {
		this.projekcje = projekcje;
	}
	public void dodajModel(Model model) {
		projekcje.add(new ProjekcjaModelu(model));
	}
	public void usunModel(Model model) {
		ProjekcjaModelu pm = getProjekcjaModelu(model);
		if (pm != null)
			projekcje.remove(pm);
	}
	public ProjekcjaModelu getProjekcjaModelu(Model model) {
		ProjekcjaModelu resoult = null;
		for (ProjekcjaModelu pm : projekcje) {
			if (pm.getModel() == model) {
				resoult = pm;
				break;
			}
		}
		return resoult;
	}
	public ProjekcjaZmiennejObjasnianej getProjekcjaZmiennejObjasnianej(Model model, String nazwaZmiennej) {
		ProjekcjaZmiennejObjasnianej resoult = null;
		ProjekcjaModelu pm = getProjekcjaModelu(model);
		if (pm != null) {
			resoult = pm.getProjekcjaZmiennejObjasnianej(nazwaZmiennej);
		}
		return resoult;
	}
	
	
	// wykonanie projekcji
	public void wykonajProjekcjeMonteCarlo(Set<Date> datySymulacji, Date poczatekSymulacji, int liczbaSymulacji) { // w dniach
		if (magazynWynikow == null)
			createMagazynWynikow(liczbaSymulacji);

		LinkedHashSet<Date> krokiSymulacji = new LinkedHashSet<Date>();
		for (Date d : datySymulacji) {
			krokiSymulacji.add(d);
		}
		Utils.sortuj(krokiSymulacji);

		for (int i=0; i<liczbaSymulacji; i++) {
			wykonajProjekcjeModeli(krokiSymulacji, poczatekSymulacji, i);
		}
	}
	public void wykonajProjekcjeMonteCarlo(Date poczatekSymulacji, Date poczatekProjekcji, Date koniecPojekcji, int dlugoscKrokuSymulacji, int liczbaSymulacji) { // w dniach
		if (magazynWynikow == null)
			createMagazynWynikow(liczbaSymulacji);
		LinkedHashSet<Date> krokiSymulacji = new LinkedHashSet<Date>();
		LinkedHashSet<Date> temp = wyznaczDatyKrokowSymulacji(poczatekProjekcji, koniecPojekcji, dlugoscKrokuSymulacji);
		if (temp.contains(poczatekSymulacji))
			temp.remove(poczatekSymulacji);
		for (Date d : temp)
			krokiSymulacji.add(d);
		
		for (int i=0; i<liczbaSymulacji; i++) {
			wykonajProjekcjeModeli(krokiSymulacji, poczatekSymulacji, i);
		}
	}
	public void wykonajProjekcjeModeli(LinkedHashSet<Date> krokiSymulacji, Date poczatekSymulacji, int numerSymulacji) {
		Map<ProjekcjaModelu,Boolean> pierwszyKrok = new HashMap<ProjekcjaModelu,Boolean>();
		for (ProjekcjaModelu pm : projekcje)
			pm.inicjalizujProjekcjeModelu(poczatekSymulacji);
		
		// projekcja w krokach
		for (Date d : krokiSymulacji) {
			// wszystkie modele wykonuja krok
			for (ProjekcjaModelu pm : projekcje) {
				Map<String, Double> krokiProjekcji = pm.wyznaczProjekcjeModelu(d);
				pm.zapiszWynikProjekcjiModelu(krokiProjekcji,d);
			}
		}
		// po zrobieniu wszystkich krokow, zapisanie wynikow dla Monte Carlo
		for (ProjekcjaModelu pm : projekcje) {
			pm.archiwizujWynikiProjekcjiModelu(magazynWynikow,numerSymulacji);
			pm.resetujWynikiProjekcjiModelu();
		}		
	}
	
	private LinkedHashSet<Date> wyznaczDatyKrokowSymulacjiZLiczby(Date poczatekProjekcji, Date koniecPojekcji, int liczbaKrokow) {
		LinkedHashSet<Date> resoult = new LinkedHashSet<Date>();
		long d1 = poczatekProjekcji.getTime();
		long d2 = koniecPojekcji.getTime();
		long krok = (d2 - d1) / (liczbaKrokow + 1); // [Miliseconds]
		
		long krokDni = krok / Utils.getDayMiliseconds();
		krokDni = krokDni + 1;
		
		resoult = wyznaczDatyKrokowSymulacji(poczatekProjekcji, koniecPojekcji, (int)krok);
		return resoult;
	}
	private LinkedHashSet<Date> wyznaczDatyKrokowSymulacji(Date poczatekProjekcji, Date koniecPojekcji, int dayFraction) {
		LinkedHashSet<Date> resoult = new LinkedHashSet<Date>();
		Date temp = poczatekProjekcji;
		while(temp.before(koniecPojekcji)) {
			resoult.add(new Date(temp.getTime()));
			temp = Utils.getNextDateDayFraction(temp, dayFraction);
		}
		koniecPojekcji = temp;
		return resoult;
	}
	
	
}

